﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;

namespace WinFromTools
{
    public partial class ParseXsdForm : Form
    {
        public ParseXsdForm()
        {
            InitializeComponent();
        }

        private void btnSelectOriginal_Click(object sender, EventArgs e)
        {
            if (folderDialog.ShowDialog() == DialogResult.OK)
            {
                txtOriginalPath.Text = folderDialog.SelectedPath;
            }
        }

        private void btnParseXsdFiles_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(txtOriginalPath.Text) || txtOriginalPath.Text.Length < 4)
            {
                MessageBox.Show("请先选择XSD文件所在目录");
            }
            else
            {
                ParseXSDFiles(txtOriginalPath.Text, txtFileNames, txtOriginalPath.Text);
                WriteLine(txtFileNames, "处理完毕！");

                MessageBox.Show("处理完毕!");
            }
        }

        public static void ParseXSDFiles(string srcPath, TextBox txtBox, string prefix = null)
        {
            string[] fileList = Directory.GetFileSystemEntries(srcPath);
            // 遍历所有的文件和目录
            foreach (string file in fileList)
            {
                // file可能为子目录，所以 先作为目录处理如果存在这个目录就递归Copy该目录下面的文件
                if (Directory.Exists(file))
                {
                    ParseXSDFiles(file, txtBox, prefix);
                }
                else
                {
                    string fileExtension = GetFileExtension(file);
                    if (String.IsNullOrEmpty(fileExtension))
                        continue;
                    if (!String.IsNullOrEmpty(fileExtension))
                    {
                        if (!ContainString(fileExtension.ToLower(), ".xsd"))
                        {
                            continue;
                        }
                    }
                    try
                    {
                        CreateHtmlFile(file, prefix);
                    }
                    catch (Exception e)
                    {

                        MessageBox.Show(e.ToString());
                    }

                    //File.Copy(file, destPath + Path.GetFileName(file), true);
                    if (txtBox != null)
                    {
                        WriteLine(txtBox, file);
                    }
                }
            }
        }

        #region Helper

        private static void WriteLine(TextBox txtBox, String text)
        {
            txtBox.Focus();
            txtBox.AppendText(text + Environment.NewLine);
            txtBox.ScrollToCaret();
            System.Windows.Forms.Application.DoEvents();
        }

        public static bool ContainString(string s, params string[] stringSet)
        {
            bool ret = false;
            foreach (string element in stringSet)
            {
                if (element.ToLower() == s.ToLower())
                {
                    ret = true;
                    break;
                }
            }

            return ret;
        }

        public static string GetFileExtension(string fileName)
        {
            int length = fileName.Length;
            int startIndex = length;
            while (--startIndex >= 0)
            {
                char ch = fileName[startIndex];
                if (ch == '.')
                {
                    return fileName.Substring(startIndex, length - startIndex);
                }
                if (((ch == Path.DirectorySeparatorChar) || (ch == Path.AltDirectorySeparatorChar)) || (ch == Path.VolumeSeparatorChar))
                {
                    break;
                }
            }
            return string.Empty;
        }

        #endregion

        #region Psrse XSD file

        private static void CreateHtmlFile(string filename, string prefix = null)
        {
            int length = 3; //@"E:\Sync\Sync\Work\Projects\Customs\03.需求\数据格式".Length;
            if (!String.IsNullOrEmpty(prefix))
            {
                length = prefix.Length;
            }
            var tables = GetTableScheme(filename);
            var html = GetHtmlString(tables, filename.Substring(length).Replace('\\', '_').Replace('.', '_'));
            System.IO.File.WriteAllText(filename.ToLower().Replace(".xsd", ".html"), html);
        }

        private static IList<TableScheme> GetTableScheme(string uri)
        {
            // E:\Sync\Sync\Work\Projects\Customs\03.需求\数据格式\1.一次申报系统数据交换报文接口v1.0\报关单\xsd\complexType.xsd
            var doc = XDocument.Load(uri);
            var xs = XNamespace.Get(@"http://www.w3.org/2001/XMLSchema");
            var decxs = XNamespace.Get(@"http://www.chinaport.gov.cn/dec");
            //var xDoc = XDocument.Load("your XSD path");
            //var ns = XNamespace.Get(@"http://www.w3.org/2001/XMLSchema");
            //var length = (from sType in xDoc.Element(ns + "schema").Elements(ns + "simpleType")
            //              where sType.Attribute("name").Value == "Amount_Type"
            //              from r in sType.Elements(ns + "restriction")
            //              select r.Element(ns + "maxLength").Attribute("value")
            //                      .Value).FirstOrDefault();

            var rootElement = from root in doc.Elements() // 根元素 xs:schema
                                                          //from o in root.Elements() // 根元素 xs:schema的所有直接子元素
                              select root;

            //var tableElement = from root in doc.Elements() // 根元素 xs:schema
            //                   from o in root.Elements() // 根元素 xs:schema的所有直接子元素
            //                   select o;

            //var tmp = rootElement.ToList();

            //var complexType = from el in rootElement.Elements(xs + "complexType")
            //                      //from o in root.Elements()
            //                  select el;
            //StringBuilder sb = new StringBuilder("");

            var tables = new List<TableScheme>();

            foreach (var table in rootElement.Elements(xs + "complexType"))
            {
                //var tableDescription = table.Element(xs + "annotation").Element(xs + "documentation");
                //var desc = tableDescription.Value;
                if (table.Attribute("name") == null)
                {
                    continue;
                }
                var tableScheme = new TableScheme
                {
                    TableName = table.Attribute("name").Value.Trim(),
                    //TableComment = 
                };
                foreach (var column in table.Elements().Elements())
                {
                    if (column.Name.LocalName == "documentation")
                    {
                        tableScheme.TableComment = column.Value;
                        continue;
                    }
                    if (column.Attribute("name") == null)
                        continue;
                    if (column.Name.LocalName == "element")
                    {
                        var columnScheme = new ColumnScheme
                        {
                            ColumnName = column.Attribute("name").Value,
                            ColumnRestriction = "",
                            Required = true
                        };
                        PrepareColumnScheme(columnScheme, xs, column);

                        tableScheme.Columns.Add(columnScheme);

                        if (column.Element(xs + "complexType") != null)
                        {
                            tables.AddRange(ParseChildElement(column, xs, tables).Except(tables));
                        }
                    }
                }
                tables.Add(tableScheme);
            }

            var external = ParseElement(rootElement, xs, tables);
            tables.AddRange(external.Except(tables));

            return tables;
        }

        private static void PrepareColumnScheme(ColumnScheme columnScheme, XNamespace xs, XElement column)
        {
            var minOccurs = column.Attribute("minOccurs") == null ? null : column.Attribute("minOccurs").Value.Trim();
            if (minOccurs == "0") // minOccurs == null || minOccurs == "0"
            {
                columnScheme.Required = false;
            }

            var annotationElement = column.Element(xs + "annotation");
            if (annotationElement != null)
            {
                columnScheme.ColumnComment = annotationElement.Element(xs + "documentation").Value;
            }
            var simpleTypeElement = column.Element(xs + "simpleType");
            if (simpleTypeElement != null)
            {
                var restrictionElement = simpleTypeElement.Element(xs + "restriction");
                if (restrictionElement != null)
                {
                    columnScheme.ColumnType = restrictionElement.Attribute("base") == null ? "xs:string" : restrictionElement.Attribute("base").Value.Trim();
                    if (restrictionElement.Element(xs + "maxLength") != null)
                    {
                        columnScheme.ColumnRestriction += "最大长度为:" + restrictionElement.Element(xs + "maxLength").Attribute("value").Value;
                    }
                    var minLengthElement = restrictionElement.Element(xs + "minLength");
                    if (minLengthElement != null)
                    {
                        var minLength = minLengthElement.Attribute("value").Value;
                        columnScheme.ColumnRestriction += "最小长度为:" + minLength;
                        if (minLength == "0")
                            columnScheme.AllowNull = true;
                    }

                    if (restrictionElement.Element(xs + "length") != null)
                    {
                        columnScheme.ColumnRestriction += "固定长度为:" + restrictionElement.Element(xs + "length").Attribute("value").Value;
                    }
                    if (restrictionElement.Element(xs + "pattern") != null)
                    {
                        columnScheme.ColumnRestriction += "格式为:" + restrictionElement.Element(xs + "pattern").Attribute("value").Value;
                    }
                    if (restrictionElement.Element(xs + "totalDigits") != null)
                    {
                        columnScheme.ColumnRestriction += "数字位数为:" + restrictionElement.Element(xs + "totalDigits").Attribute("value").Value;
                    }
                    if (restrictionElement.Element(xs + "fractionDigits") != null)
                    {
                        columnScheme.ColumnRestriction += "其中小数位数为:" + restrictionElement.Element(xs + "fractionDigits").Attribute("value").Value;
                    }
                }
            }
        }

        private static IList<TableScheme> ParseElement(IEnumerable<XElement> element, XNamespace xs, IEnumerable<TableScheme> existed)
        {
            var tables = new List<TableScheme>();
            foreach (var table in element.Elements(xs + "element"))
            {
                if (table.Attribute("name") == null)
                {
                    continue;
                }
                if (existed.Union(tables).FirstOrDefault(t => t.TableName == table.Attribute("name").Value.Trim()) != null)
                    continue;
                var tableScheme = new TableScheme
                {
                    TableName = table.Attribute("name").Value.Trim(),
                    //TableComment = 
                };
                var annotationElement = table.Element(xs + "annotation");
                if (annotationElement == null)
                    continue;
                if (annotationElement.Element(xs + "documentation") == null)
                    continue;
                if (annotationElement != null)
                {
                    tableScheme.TableComment = annotationElement.Element(xs + "documentation").Value;
                }
                if (table.Element(xs + "complexType") == null)
                    continue;
                if (table.Element(xs + "complexType").Elements() == null)
                    continue;
                if (table.Element(xs + "complexType").Elements().Elements(xs + "element") == null)
                    continue;

                foreach (var column in table.Element(xs + "complexType").Elements().Elements(xs + "element"))
                {
                    if (column.Name.LocalName == "documentation")
                    {
                        tableScheme.TableComment = column.Value;
                        continue;
                    }
                    if (column.Name.LocalName == "element")
                    {
                        if (column.Attribute("name") == null)
                            continue;
                        var columnScheme = new ColumnScheme
                        {
                            ColumnName = column.Attribute("name").Value,
                            ColumnRestriction = "",
                            Required = true
                        };
                        PrepareColumnScheme(columnScheme, xs, column);

                        var type = column.Attribute("type");
                        if (type != null)
                        {
                            var decxs = XNamespace.Get(@"http://www.chinaport.gov.cn/dec");
                            columnScheme.ColumnType = type.Value;

                            columnScheme.Referance = type.Value.IndexOf(':') > 0 ? type.Value.Substring(type.Value.IndexOf(':') + 1) : type.Value;
                        }

                        tableScheme.Columns.Add(columnScheme);

                        if (column.Element(xs + "complexType") != null)
                        {
                            tables.AddRange(ParseChildElement(column, xs, tables.Union(existed)));
                        }
                    }

                    tables.Add(tableScheme);
                }
            }

            return tables;
        }

        private static IList<TableScheme> ParseChildElement(XElement element, XNamespace xs, IEnumerable<TableScheme> existed)
        {
            var tables = new List<TableScheme>();

            foreach (var table in element.Element(xs + "complexType").Elements().Elements(xs + "element"))
            {
                if (table.Attribute("name") == null)
                {
                    continue;
                }

                if (table.Attribute("type") != null)
                {
                    continue;
                }
                if (existed.Union(tables).FirstOrDefault(t => t.TableName == table.Attribute("name").Value.Trim()) != null)
                    continue;

                var tableScheme = new TableScheme
                {
                    TableName = table.Attribute("name").Value.Trim(),
                    //TableComment = 
                };
                var annotationElement = table.Element(xs + "annotation");
                if (annotationElement != null)
                {
                    tableScheme.TableComment = annotationElement.Element(xs + "documentation").Value;
                }
                var complexTypeElement = table.Element(xs + "complexType");
                if (complexTypeElement != null)
                {
                    foreach (var column in complexTypeElement.Elements().Elements())
                    {
                        if (column.Attribute("name") == null)
                        {
                            continue;
                        }
                        if (column.Name.LocalName == "documentation")
                        {
                            tableScheme.TableComment = column.Value;
                            continue;
                        }
                        if (column.Name.LocalName == "element")
                        {
                            var columnScheme = new ColumnScheme
                            {
                                ColumnName = column.Attribute("name").Value,
                                ColumnRestriction = "",
                                Required = true
                            };
                            PrepareColumnScheme(columnScheme, xs, column);

                            tableScheme.Columns.Add(columnScheme);
                        }
                    }
                }
                if (tableScheme.Columns.Count > 0)
                    tables.Add(tableScheme);
            }
            return tables;
        }

        #endregion

        #region Html

        private static string GetHtmlString(IList<TableScheme> tables, string title)
        {
            var sb = new StringBuilder(@"<ul class=""list-group"" style=""margin:20px;"">");
            foreach (var table in tables.GroupBy(t => t.TableName).Select(g => g.First()))
            {
                //sb.AppendLine(GetTableHtml(table));
                sb.AppendLine($@"<li class=""list-group-item""><a href=""#{table.TableName}"">{table.TableComment} ( {table.TableName })</a></li>");
            }
            sb.AppendLine("</ul>");

            foreach (var table in tables)
            {
                sb.AppendLine(GetTableHtml(table));
            }

            return $@"<!DOCTYPE html>
<html lang=""zh-CN"">
  <head>
    <meta charset=""utf-8"">
    <meta http-equiv=""X-UA-Compatible"" content=""IE=edge"">
    <meta name=""viewport"" content=""width=device-width, initial-scale=1"">
    <title>{title}</title>
	<link href=""https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"" rel=""stylesheet"">
    <!--[if lt IE 9]>
      <script src=""https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js""></script>
      <script src=""https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js""></script>
    <![endif]-->
  </head>
  <body>
    <h1>{title}</h1>{sb.ToString()}<script src=""https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js""></script>
    <script src=""https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js""></script>
  </body>
</html>";


        }
        private static string GetTableHtml(TableScheme table)
        {
            var sb = new StringBuilder("");
            int colunmIndex = 1;
            foreach (var col in table.Columns)
            {
                sb.AppendLine(GetRowHtml(col, colunmIndex++));
            }
            //  id=""tables-contextual-classes""
            // <th>可选</th>
            // <th> 必填</th>
            return $@"<div style=""margin:20px;""><h2 id=""{table.TableName}"">{table.TableName}</h2><p>{table.TableComment}</p></div>
            <div style=""margin:20px;"">
            <table class=""table table-bordered"">
                <thead>
	            <tr>
	                <th>#</th>
	                <th>字段名</th>
                    <th style=""max-width:300px"">说明</th>
	                <th>类型</th>
	                
	                <th>限制</th>
	  
	            </tr>
                </thead>
                <tbody>{sb.ToString()} </tbody>
            </table></div>";
        }
        private static string GetRowHtml(ColumnScheme row, int index)
        {
            if (!String.IsNullOrEmpty(row.Referance) && String.IsNullOrEmpty(row.ColumnRestriction))
            {
                row.ColumnRestriction = $@"<a href=""{row.Referance}"">{row.Referance}</a>";
            }
            return $@"<tr>
	          <th scope=""row"">{index}</th>
	          <td>{row.ColumnName}</td>
	          <td style=""max-width:300px"">{row.ColumnComment}</td>
	          <td>{row.ColumnType}</td>
	          <td>{row.ColumnRestriction}</td>
	        </tr>";
        }
        // <td>{row.Required}</td>
        // <td>{!row.AllowNull}</td>

        #endregion

        #region Nested Classes

        internal partial class TableScheme
        {
            public string TableName { get; set; }
            public string TableComment { get; set; }

            private IList<ColumnScheme> _colunms;
            public IList<ColumnScheme> Columns
            {
                get { return _colunms ?? (_colunms = new List<ColumnScheme>()); }
                set { _colunms = value; }
            }
        }

        internal partial class ColumnScheme
        {
            public string ColumnName { get; set; }
            public string ColumnComment { get; set; }
            public string ColumnType { get; set; }

            /// <summary>
            /// 参考子类型
            /// </summary>
            public string Referance { get; set; }

            public string ColumnLength { get; set; }
            public string ColumnRestriction { get; set; }
            public bool AllowNull { get; set; }
            public bool Required { get; set; }

        }


        #endregion    
    }
}
